﻿/**************************************************************************
创建时间:	2021/9/28 10:18
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com

Copyright (c) zhcun.cn

描	述：串口服务基类
记	录：
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZhCun.SerialIO.Filters
{
    public abstract class ReceiveBaseFilter : IReceiveFilter
    {
        /// <summary>
        /// 数据包的最小长度，小于该长度等下次解析处理
        /// </summary>
        protected internal abstract int MinLength { get; }
        /// <summary>
        /// 当解析完成后
        /// </summary>
        public Action<PackageInfo> OnFilterFinish { set; get; }
        /// <summary>
        /// 查找包头的位置，如果无固定包头返回 offset
        /// </summary>
        /// <param name="data">源数据</param>
        /// <param name="offset">源数据偏移量</param>
        /// <param name="count">源数据长度</param>
        /// <param name="headOffset">返回的包头位置</param>
        /// <returns></returns>
        protected internal abstract bool FindHead(byte[] data, int offset, int count, out int headOffset);
        /// <summary>
        /// 获取数据包Key(命令字)
        /// </summary>
        protected internal abstract int GetPackageKey(byte[] data, int offset, int count);
        /// <summary>
        /// 获取数据总长度（包含头及校验码）
        /// </summary>
        protected internal abstract int GetDataLength(byte[] data, int offset, int count);
        /// <summary>
        /// 过滤协议，粘包、分包的处理
        /// </summary>
        public virtual void Filter(IBufferReceive recBuffer, byte[] data, int offset, int count, bool isBuffer, out int rest)
        {
            if (!isBuffer && recBuffer.HasReceiveBuffer())
            {
                recBuffer.SetReceiveBuffer(data, offset, count);
                Filter(recBuffer, recBuffer.ReceiveBuffer, 0, recBuffer.ReceiveOffset, true, out rest);
                return;
            }

            if (isBuffer && count < MinLength)
            {
                rest = 0;
                return;
            }

            if (!isBuffer && recBuffer.ReceiveOffset + count < MinLength)
            {
                //等下一次接收后处理
                recBuffer.SetReceiveBuffer(data, offset, count);
                rest = 0;
                return;
            }

            rest = 0;

            if (!FindHead(data, offset, count, out int headOffset))
            {
                //未找到包头丢弃
                recBuffer.RestReceiveBuffer();
                FilterFinish(1, data, offset, count);
                return;
            }

            if (count - (headOffset - offset) < MinLength)
            {
                // 从包头位置小于最小长度（半包情况），注意：解析了一半，不做解析完成处理
                recBuffer.SetReceiveBuffer(data, headOffset, count - (headOffset - offset));
                return;
            }

            int dataLen = GetDataLength(data, headOffset, count - (headOffset - offset));
            if (dataLen <= 0)
            {
                //错误的长度 丢弃
                recBuffer.RestReceiveBuffer();
                FilterFinish(2, data, offset, count);
                return;
            }

            if (dataLen > count - (headOffset - offset))
            {
                //半（分）包情况，等下次接收后合并
                if (!isBuffer) recBuffer.SetReceiveBuffer(data, headOffset, count - (headOffset - offset));
                return;
            }

            rest = count - (dataLen + (headOffset - offset));

            FilterFinish(0, data, headOffset, dataLen);

            recBuffer.RestReceiveBuffer();
                        
            if (rest > 0)
            {
                Filter(recBuffer, data, headOffset + dataLen, rest, false, out rest);
                return;
            }
        }
        /// <summary>
        /// 过滤完成处理
        /// </summary>
        /// <param name="status">状态，0：正常，1：未找到包头丢弃，2: 错误的长度</param>
        protected virtual void FilterFinish(int status, byte[] data, int offset, int count)
        {
            if (OnFilterFinish != null)
            {
                var r = new PackageInfo(0, data, offset, count);
                r.Status = status;
                if (status == 0)
                {
                    r.Key = GetPackageKey(data, offset, count);
                }
                OnFilterFinish(r);
            }
        }
    }
}
